﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Net;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.Diagnostics.Contracts;
using System.Windows.Controls;
using System.Collections.Specialized;
using ShredTheLog.Common.MVVM;
using ShredTheLog.Model;
using System.Globalization;
using System.Diagnostics;
using ShredTheLog.Common;
using ShredTheLog.Api.DataContracts;
using ShredTheLog.Api.DataContracts.TextChat;
using ShredTheLog.Api;
using System.ComponentModel.Composition;
using ShredTheLog.Views;
using System.Windows.Threading;

namespace ShredTheLog.ViewModels
{
    public sealed class MainViewModel : ViewModelBase
    {
        [Import]
        private IShredServices ShredServices { get; set; }

        [ImportMany]
        private Lazy<ITheme, IOrderMetadata>[] _themes = null;
        public IEnumerable<ITheme> Themes 
        {
            get 
            {
                Contract.Assume(_themes != null);
                return from i in _themes orderby i.Metadata.Order select i.Value; 
            }
        }

        [ImportMany]
        private Lazy<IUiLanguage, IOrderMetadata>[] _uiLanguages = null;
        public IEnumerable<IUiLanguage> UiLanguages 
        {
            get 
            {
                Contract.Assume(_uiLanguages != null);
                return from i in _uiLanguages orderby i.Metadata.Order select i.Value; 
            }
        }

        [Import]
        private IUiSettingsSection UiSettings { get; set; }

        [Import]
        private IConnectionSettingsSection ConnectionSettings { get; set; }

        [ImportMany]
        private Lazy<ICommandListItem, IMenuCommandMetadata>[] _menuCommands = null;
        public IEnumerable<Lazy<ICommandListItem, IMenuCommandMetadata>> FileMenuCommands 
        { 
            get 
            {
                Contract.Assume(_menuCommands != null);
                return _menuCommands.Where(i => i.Metadata.Menu == Menus.File); 
            } 
        }
        public IEnumerable<Lazy<ICommandListItem, IMenuCommandMetadata>> ViewMenuCommands 
        { 
            get 
            {
                Contract.Assume(_menuCommands != null);
                return _menuCommands.Where(i => i.Metadata.Menu == Menus.View); 
            } 
        }
        public IEnumerable<Lazy<ICommandListItem, IMenuCommandMetadata>> ToolsMenuCommands 
        { 
            get 
            {
                Contract.Assume(_menuCommands != null);
                return _menuCommands.Where(i => i.Metadata.Menu == Menus.Tools); 
            } 
        }
        public IEnumerable<Lazy<ICommandListItem, IMenuCommandMetadata>> HelpMenuCommands 
        { 
            get 
            {
                Contract.Assume(_menuCommands != null);
                return _menuCommands.Where(i => i.Metadata.Menu == Menus.Help); 
            } 
        }

        [Import]
        public IShowLogInDialogCommand ShowLogInDialogCommand 
        {
            get { return _showLogInDialogCommand; }
            private set
            {
                _showLogInDialogCommand = value;
                _showLogInDialogCommand.CredentialsChanged +=
                    (sender, e) =>
                    {
                        if (TextChat != null)
                            TextChat.Dispose();
                        if (ContactList != null)
                            ContactList.Dispose();
                        _internalContacts.Clear();
                        OnPropertyChanged(() => Credentials);

                        if (ShowLogInDialogCommand.Credentials != null)
                            UpdateContactList();
                    };
            }
        }
        private IShowLogInDialogCommand _showLogInDialogCommand;

        [Import]
        public ILogOutCommand LogOutCommand { get; private set; }

        [Import]
        public IGoToTextChatCommand GoToTextChatCommand 
        {
            get { return _goToTextChatCommand; }
            private set
            {
                _goToTextChatCommand = value;
                _goToTextChatCommand.Completed +=
                    (sender, e) =>
                    {
                        if (TextChat == null)
                        {
                            TextChat = new TextChatViewModel();
                            TextChat.Closed += (sender1, e1) => TextChat = null;
                        }
                        SelectPage(TextChat);
                    };
            }
        }
        private IGoToTextChatCommand _goToTextChatCommand;

        [Import]
        public IGoToContactListCommand GoToContactListCommand
        {
            get { return _goToContactListCommand; }
            private set
            {
                _goToContactListCommand = value;
                _goToContactListCommand.Completed +=
                    (sender, e) =>
                    {
                        if (ContactList == null)
                        {
                            ContactList = new ContactListViewModel();
                            EventHandler onClosed = null;
                            onClosed =
                                (sender1, e1) =>
                                {
                                    ContactList.Closed -= onClosed;
                                    ContactList = null;
                                };
                            ContactList.Closed += onClosed;
                        }
                        SelectPage(ContactList);
                    };
            }
        }
        private IGoToContactListCommand _goToContactListCommand;

        [Import]
        public IGoToUserSearchCommand GoToUserSearchCommand
        {
            get { return _goToUserSearchCommand; }
            private set
            {
                _goToUserSearchCommand = value;
                _goToUserSearchCommand.Completed +=
                    (sender, e) =>
                    {
                        if (UserSearch == null)
                        {
                            UserSearch = new UserSearchViewModel();
                            EventHandler onClosed = null;
                            onClosed =
                                (sender1, e1) =>
                                {
                                    UserSearch.Closed -= onClosed;
                                    UserSearch = null;
                                };
                            UserSearch.Closed += onClosed;
                        }
                        SelectPage(UserSearch);
                    };
            }
        }
        private IGoToUserSearchCommand _goToUserSearchCommand;

        [ImportMany]
        private Lazy<ICommandListItem, IMenuCommandMetadata>[] _joinPublicChatRoomCommands = null;
        public IEnumerable<Lazy<ICommandListItem, IMenuCommandMetadata>> JoinPublicChatRoomCommands 
        { 
            get 
            {
                Contract.Assume(_joinPublicChatRoomCommands != null);
                return _joinPublicChatRoomCommands.Where(i => i.Metadata.Menu == Menus.Rooms); 
            } 
        }

        [Import]
        private IAddContactCommand AddContactCommand
        {
            get { return _addContactCommand; }
            set
            {
                _addContactCommand = value;
                _addContactCommand.Completed += (sender, e) => UpdateContactList();
            }
        }
        private IAddContactCommand _addContactCommand;

        [Import]
        private IDeleteContactCommand DeleteContactCommand
        {
            get { return _deleteContactCommand; }
            set
            {
                _deleteContactCommand = value;
                _deleteContactCommand.Completed += (sender, e) => UpdateContactList();
            }
        }
        private IDeleteContactCommand _deleteContactCommand;

        [ContractInvariantMethod]
        void ObjectInvariant()
        {
            Contract.Invariant(Contacts != null);
        }

        public MainViewModel()
        {
            _internalContacts = new ObservableCollection<IContact>();
            Contacts = new ReadOnlyObservableCollection<IContact>(_internalContacts);

            HomePage = new HomePageViewModel();

            _internalPages = new ObservableCollection<ViewModelBase> { HomePage };
            _pages = new ReadOnlyObservableCollection<ViewModelBase>(_internalPages);
        }

        public ISessionCredentials Credentials { get { return ShowLogInDialogCommand.Credentials; } }

        private void UpdateContactList()
        {
            Contract.Assume(ShowLogInDialogCommand.Credentials != null);

            Action<Exception> onError = ex => ShredServices.ShowError(() => "Failed to get contact list.", ex);
            var credentials = ShowLogInDialogCommand.Credentials;
            Contract.Assume(credentials != null);
            ConnectionSettings.GetClient().GetContactList(credentials).ObserveOn(ShredServices.Dispatcher).Subscribe(
                contacts =>
                {
                    try
                    {
                        // Поставим здесь повторную проверку на случай если за время получения списка контактов пользователь уже успел разлогиниться.
                        if (ShowLogInDialogCommand.Credentials != null)
                            _internalContacts.Merge(contacts, c => c);
                    }
                    catch (Exception ex)
                    {
                        onError(ex);
                    }
                },
                onError);
        }

        private void SelectPage(ViewModelBase page)
        {
            Contract.Requires(page != null);

            var view = CollectionViewSource.GetDefaultView(Pages);
            view.MoveCurrentTo(page);
        }

        private ObservableCollection<ViewModelBase> _internalPages;
        private ReadOnlyObservableCollection<ViewModelBase> _pages;
        public ReadOnlyObservableCollection<ViewModelBase> Pages { get { return _pages; } }

        public HomePageViewModel HomePage { get; private set; }

        private TextChatViewModel _textChat;
        public TextChatViewModel TextChat
        {
            get { return _textChat; }
            private set
            {
                if (_textChat != null)
                    _internalPages.Remove(_textChat);
                _textChat = value;
                if (_textChat != null)
                    _internalPages.Add(_textChat);
                OnPropertyChanged(() => TextChat);
            }
        }

        private ContactListViewModel _contactList;
        public ContactListViewModel ContactList
        {
            get { return _contactList; }
            private set
            {
                if (_contactList != null)
                    _internalPages.Remove(_contactList);
                _contactList = value;
                if (_contactList != null)
                    _internalPages.Add(_contactList);
            }
        }

        private UserSearchViewModel _userSearch;
        public UserSearchViewModel UserSearch
        {
            get { return _userSearch; }
            private set
            {
                if (_userSearch != null)
                    _internalPages.Remove(_userSearch);
                _userSearch = value;
                if (_userSearch != null)
                    _internalPages.Add(_userSearch);
            }
        }

        private VersionInfo _versionInfo = new VersionInfo();

        public string Product { get { return _versionInfo.Product; } }

        private bool _isWindowClosed;
        public bool IsWindowClosed
        {
            get { return _isWindowClosed; }
            set 
            {
                _isWindowClosed = value;
                OnPropertyChanged(() => IsWindowClosed);
                if (IsWindowClosed)
                    ShredServices.Shutdown();
            }
        }

        private ObservableCollection<IContact> _internalContacts;
        public ReadOnlyObservableCollection<IContact> Contacts { get; private set; }
    }
}
